/*
 * Copyright (c) 2009, The haXe Project Contributors
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

/**
	The utility Sugar classes provide additional Iterable manipulation routines
**/


/**
	The StatTools class adds additional common stat utility functions.
**/

/*package org.sugar.utilities;*/
import HashTools;
import ArrayTools;

class StatTools{

/**
	Returns the mean from an Iterable of Floats.
**/
	public static function mean (it:Iterable<Float>) : Null<Float>{
		var sum:Float = 0;
		var count:Int = 0;
		for (i in it){
			sum+=i;
			count++;
		}
		if (count == 0){return null;}
		return sum/count;
	}
	
/**
	Returns the sum of an Iterable of Floats.
**/
	public static function sum (it:Iterable<Float>) : Float{
		var sum:Float = 0;
		for (i in it){
			sum+=i;
		}
		return sum;
	}

/**
	Returns the min of an Iterable of Floats.
**/	
	public static function min (it:Iterable<Float>) : Null<Float>{
		var min:Float = Math.POSITIVE_INFINITY;
		for (i in it){
			if (i < min) min = i;
		}
		if (min == Math.POSITIVE_INFINITY) return null;
		return min;		
	}

/**
	Returns the max of an Iterable of Floats.
**/	
	public static function max (it:Iterable<Float>) : Null<Float>{
		var max:Float = Math.NEGATIVE_INFINITY;
		for (i in it){
			if (i > max) max = i;
		}
		if (max == Math.NEGATIVE_INFINITY) return null;
		return max;		
	}
	
/**
	Maps an Iterable of Ints into an Iterable of Floats.
**/	
	public static function floatMap(it:Iterable<Int>):List<Float>{
		var l = new List<Float>();
		for (i in it){
			l.add(i);
		}
		return l;
	}
	
/**
	Finds the standard deviation of an Iterable of Floats.  It is possible to set the mean, or to adjust
	for a sampled distribution using the additional arguments.
**/	
	public static function stdev (it:Iterable<Float>,?set_mean:Float,?from_sample:Bool = false) : Null<Float>{
		var sum:Float = 0;
		var mean:Float;
		var count:Int = 0;
		if (set_mean != null){mean = set_mean;}
		else{mean = StatTools.mean(it);}
		var r = Lambda.fold( it, 
									function(item:Float,accum:Float) {
										accum += Math.pow(item-mean,2);
										count++;
										return accum;
									},
									0.0);
					
		if (from_sample){
			if (count <=1){return null;}
			r*= 1/(count-1);
			}
			
	   r = Math.pow(r,.5);
		return r;
	}
	
/**
	Returns the median from an Iterable of Floats.
**/	
	public static function median (it:Iterable<Float>) : Null<Float>{
		var a = ArrayTools.fastCreateArray(it);
		if (a.length == 0) return null;
		var f = function(x:Float,y:Float){ 
				if (x > y) return 1;
				if (x < y) return -1;
				else return 0;
			}
		a.sort(f);
		var index:Float = Math.floor(a.length/2);
		if (a.length % 2 == 1) return a[Std.int(index)];
		else return (a[Std.int(index)] + a[Std.int(index+1)])/2;
	}
	
/**
	Returns the mode from an Iterable of Floats.
**/	
	public static function mode (it:Iterable<Float>) : Null<Float>{
		var g = HashTools.groupByHash(it);
		var gm = HashTools.hashValueMap(g, function(x) return Lambda.count(x)); 
		g = null;
		var a = new Array<Float>();
	   for (i in gm) a.push(i);
		if (a.length == 0) return null;
		gm = null;
		var f = function(x:Float, y:Float){
			if (x > y) return 1;
			else if (y < x) return -1;
			else return 0;
		}
		a.sort(f);
		return a[0];
	}
	
	
}